import java.util.*;
import java.util.regex.*;


public class Driver
{
	public static final int NUMBER_OF_NODES = 111;
	
	public static void main(String [] args) throws InterruptedException
	{
		Thread [] nodes = new Thread[NUMBER_OF_NODES];
        Node [] nodePtr = generateNodes(2, 11, 10);
        
        for(int i = 0; i < NUMBER_OF_NODES; i++)
        {
        	if(i != 0)
        	{
        		((ClusterNode)nodePtr[i]).setPeers(((ClusterNode)nodePtr[i]).parent.childrenId);
        		((ClusterNode)nodePtr[i]).setParentPeers(((ClusterNode)nodePtr[i]).parent.peerId);
        	}
        	nodes[i] = new Thread(nodePtr[i]);
        }
        
        /*
         * Done in generateNodes()
		nodePtr[0] = generateNode(1,0);
		nodes[0] = new Thread(nodePtr[0]);
		
        // create 10 nodes all connected to 0
        for(int i = 1; i < 10; i++)
		{
			nodes[i] = new Thread(nodePtr[i] = generateNode(1,i, nodePtr[0]));
            
            // make fully connected
            
            for(int j = 0; j < i; j++)
                nodePtr[i].fullConnect(nodePtr[j]);
            
		}
        */
        
        /* debug, print outgoing connections
        for(int i = 0; i < nodePtr.length; i++)
        {
            System.out.print(i + " (" + nodePtr[i].commChannels.size() + "): ");
            Integer[] arr = new Integer[nodePtr[i].commChannels.size()];
            arr = nodePtr[i].commChannels.keySet().toArray(arr);
            
            for(int j = 0; j < arr.length; j++)
                System.out.print( arr[j] + " ");
            System.out.println();
        }*/
        
        
        // start the simulation
		for(Thread t : nodes)
		{
			t.start();
		}
        
        // wait
        Scanner scan = new Scanner(System.in);
        scan.nextLine();
        
        // close down the nodes
        for(Node n : nodePtr)
        {
            n.stopRunning(); // stops execution of the node
        }
        
        // wait 6 seconds for all the nodes to stop
        try
        {
            for(int i = 0; i < 12; i++)
            {
                Thread.sleep(500);
                System.out.print(".");
            }
            System.out.println();
        }catch(InterruptedException e)
        {
            e.printStackTrace();
        }
        
        
        // gather statistical information
        
        int i;
        long totalCopies = 0;  // total number of copies recieved
        int totalMessages = 0; // total # of unique data messages
        int totalRecieved = 0; // total # of unique messages recieved by each node
        double totalCoverage = 0;
        for(Node n : nodePtr)
        {
           totalCopies += n.countCopies();
           totalMessages += n.countOrigin();
           totalRecieved += n.countRecieves();
           totalCoverage += n.getCoverage();
           System.out.println("n receives:  " + n.countRecieves());
           System.out.println("n generated: " + n.countOrigin());
           System.out.println("n copies:    " + n.countCopies());
           System.out.println();
        }
        
        // save statistiscal info
        System.out.println("Unique messages: " + totalMessages + " " +
                totalRecieved);
        System.out.println();
        System.out.println("Copy ratio: " + ((double)totalCopies/totalMessages));
        System.out.println("Coverage: " + 
            totalCoverage/nodePtr.length);
        
        // end main
	}
    
    public static Node generateNode(int type, int ID, Node connect)
    {
        EpidemicData dat = new EpidemicData();
        dat.fanOut = 6;
        dat.repeat = 2;
        dat.bufSize = 75;
        
        if(type == 0)
            return new Node(ID,connect);
        
        if(type == 1)
            return new EpidemicNode(ID,connect,dat);
        
        if(type == 2)
            return new ClusterNode(ID,connect);
        
        return null;
    }
    
    public static Node [] generateNodes(int type, int secondLevel, int thirdLevel)
    {
    	Node [] nodeArray = new Node [NUMBER_OF_NODES];
    	nodeArray[0] = generateNode(type,0);
    	for(int i = 1; i < secondLevel; i++)
    	{
    		System.out.println(i);
    		nodeArray[i] = generateNode(type,i,nodeArray[0]);
    		for(int j = 0; j < i; j++)
    		{
                nodeArray[i].fullConnect(nodeArray[j]);
    		}
    	}
    	
    	for(int i = 0; i < secondLevel-1; i++)
    	{
    		for(int j = 0; j < thirdLevel; j++)
    		{
    			System.out.println((i)*thirdLevel+j+secondLevel);
    			nodeArray[(i)*thirdLevel+j+secondLevel] = generateNode(type, (i)*thirdLevel+j+secondLevel, nodeArray[i+1]);
    			for(int k = 0; k < (i)*thirdLevel+j+secondLevel; k++)
    			{
    				nodeArray[(i)*thirdLevel+j+secondLevel].fullConnect(nodeArray[k]);
    			}
    		}
    	}
    	
		return nodeArray;
    }
    
    public static Node generateNode(int type, int ID)
    {
        EpidemicData dat = new EpidemicData();
        dat.fanOut = 6;
        dat.repeat = 2;
        dat.bufSize = 75;
        
        if(type == 0)
            return new Node(ID);
        
        if(type == 1)
            return new EpidemicNode(ID,dat);
        
        if(type == 2)
            return new ClusterNode(ID);
        
        return null;
    }
    
}
